//: -version=TEST http_parser.obj -debug

module httparser ;

enum {	//http_method
	HTTP_DELETE	= 0x0001
	, HTTP_GET	= 0x0002
	, HTTP_HEAD	= 0x0004
	, HTTP_POST	= 0x0008
	, HTTP_PUT	= 0x0010
	/* pathological */
	, HTTP_CONNECT	= 0x0020
	, HTTP_OPTIONS	= 0x0040
	, HTTP_TRACE	= 0x0080
	/* webdav */
	, HTTP_COPY	= 0x0100
	, HTTP_LOCK	= 0x0200
	, HTTP_MKCOL 	= 0x0400
	, HTTP_MOVE	= 0x0800
	, HTTP_PROPFIND	= 0x1000
	, HTTP_PROPPATCH	= 0x2000
	, HTTP_UNLOCK	= 0x4000
}
enum { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH } // http_parser_type
const int HTTP_PARSER_MAX_METHOD_LEN 	= 10 ;

struct http_parser {
	/*
		unsigned char type : 2;
		unsigned char flags : 6;
	*/
	ubyte	__bit;
	
	ubyte	state;
	ubyte	header_state;
	ubyte	index;
	
	uint		nread;
	long		content_length;
	
	ushort	http_major;
	ushort	http_minor;
	ushort	status_code;
	ubyte	method;
	
	char		upgrade;
	void*	data;
	static assert(http_parser.sizeof is 32 );
}

alias extern(C) int function(http_parser*, char*,size_t) http_data_cb ;
alias extern(C) int function(http_parser*) http_cb ;

struct http_parser_settings {
	http_cb		on_message_begin;
	http_data_cb	on_path;
	http_data_cb	on_query_string;
	http_data_cb	on_url;
	http_data_cb	on_fragment;
	http_data_cb	on_header_field;
	http_data_cb	on_header_value;
	http_cb		on_headers_complete;
	http_data_cb	on_body;
	http_cb		on_message_complete;
	static assert(http_parser_settings.sizeof is 40 );
}

extern(C){
	size_t http_parser_execute(http_parser*, http_parser_settings*, char*, size_t );
	int	http_should_keep_alive(http_parser*);
	void	http_parser_init(http_parser*, uint);
}


static char[][] Stat = [
"s_none",
"s_dead",
"s_start_req_or_res",
"s_res_or_resp_H",
"s_start_res",
"s_res_H",
"s_res_HT",
"s_res_HTT",
"s_res_HTTP",
"s_res_first_http_major",
"s_res_http_major",
"s_res_first_http_minor",
"s_res_http_minor",
"s_res_first_status_code",
"s_res_status_code",
"s_res_status",
"s_res_line_almost_done",

"s_start_req",

"s_req_method",
"s_req_spaces_before_url",
"s_req_schema",
"s_req_schema_slash",
"s_req_schema_slash_slash",
"s_req_host",
"s_req_port",
"s_req_path",
"s_req_query_string_start",
"s_req_query_string",
"s_req_fragment_start",
"s_req_fragment",
"s_req_http_start",
"s_req_http_H",
"s_req_http_HT",
"s_req_http_HTT",
"s_req_http_HTTP",
"s_req_first_http_major",
"s_req_http_major",
"s_req_first_http_minor",
"s_req_http_minor",
"s_req_line_almost_done",

"s_header_field_start",
"s_header_field",
"s_header_value_start",
"s_header_value",
"s_header_almost_done",
"s_headers_almost_done",
"s_chunk_size_start",
"s_chunk_size",
"s_chunk_size_almost_done",
"s_chunk_parameters",
"s_chunk_data",
"s_chunk_data_almost_done",
"s_chunk_data_done",

"s_body_identity",
"s_body_identity_eof",
];

version(TEST){
	import tango.io.Stdout;
	
	struct Node {
		char[]	name ;
		
		http_cb		on_message_begin	= &_message_begin ;
		http_data_cb	on_path			= &_path ;
		http_data_cb	on_query_string		= &_query_string ;
		http_data_cb	on_url			= &_url ;
		http_data_cb	on_fragment		= &_fragment ;
		http_data_cb	on_header_field		= &_header_field ;
		http_data_cb	on_header_value		= &_header_value ;
		http_cb		on_headers_complete	= &_headers_complete ;
		http_data_cb	on_body			= &_body ;
		http_cb		on_message_complete	= &_message_complete ;
		
		http_parser		hp ;

		http_parser_settings* hps(){
			return cast(http_parser_settings*) ((cast(uint) this) + on_message_begin.offsetof );
		}
		
		static Node* Init(int type = 0 ){
			Node* p	= new Node;
			http_parser_init(&p.hp, type);
			p.hp.data	= p ;
			return p ;
		}
		
		static extern(C) int _message_begin(http_parser* _hp){
			Stdout.formatln("{}: message_begin", __LINE__ );
			return 0 ;
		}
		
		static extern(C) int _path(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: path = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}

		static extern(C) int _query_string(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: query_string = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _url(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: url = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _fragment(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: fragment = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _header_field(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: header_field = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _header_value(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: header_value = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _body(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: body = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _headers_complete(http_parser* _hp){
			Stdout.formatln("{}: headers_complete", __LINE__ );
			return 0 ;
		}
		
		static extern(C) int _message_complete(http_parser* _hp){
			Stdout.formatln("{}: message_complete", __LINE__ );
			return 0 ;
		}
	}
	auto da	= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
         "Accept: */*\r\n"
         "Transfer-Encoding: identity\r\n"
         "Content-Length: 6\r\n"
         "\r\n"
         " World" ;
	
	void main(){
		
		
		Node* n1	= Node.Init(HTTP_REQUEST) ;
		auto hps	= n1.hps ;
		
		Stdout.formatln("on_message_begin:{} = {} ", hps.on_message_begin, &Node._message_begin );
		Stdout.formatln("on_path:{}", hps.on_path );
		Stdout.formatln("on_url:{}", hps.on_url );
		Stdout.formatln("on_fragment:{}", hps.on_fragment );
		Stdout.formatln("on_header_field:{}", hps.on_header_field );
		
		
		Stdout.formatln("on_header_value:{}", hps.on_header_value );
		Stdout.formatln("on_headers_complete:{}", hps.on_headers_complete );
		Stdout.formatln("on_body:{}", hps.on_body );
		Stdout.formatln("on_message_complete:{}", hps.on_message_complete );
		
		
		Stdout.formatln("\nhttp_parser:{}", &n1.hp);
		Stdout.formatln("http_parser_settings:{}\n", n1.hps).flush;
		
		//Stdout.formatln("da:{}\n", da).flush;
		int off	= da.length /19* 18 ;
		char[] d1	= da[0..off];
		char[] d2	= da[off..$];
		int i	= http_parser_execute(&n1.hp, n1.hps, &d1[0], d1.length  );
		
		Stdout.formatln("`{}`", d2);
		assert(i is d1.length);
		Stdout.formatln("{}: stat = `{}:{}`\n", __LINE__, Stat[n1.hp.state], n1.hp.state );
		
		i	= http_parser_execute(&n1.hp, n1.hps, &d2[0], d2.length  );
		assert(i is d2.length);
		Stdout.formatln("{}: stat = `{}:{}`", __LINE__, Stat[n1.hp.state], n1.hp.state );
		Stdout.formatln("{}: header_state = `{}:{}`", __LINE__, Stat[n1.hp.header_state], n1.hp.header_state );
		
	}
}